﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Collections.Specialized;
using System.Timers;
using Microsoft.Win32;
using System.Text.RegularExpressions;

namespace GuildLaunchProfileUPLOADER
{
    public partial class Form1 : Form
    {
        private int iFileSystemNotification;
        private StringCollection strListFilesToUpdate = new StringCollection();
        private StringCollection watchedFiles = new StringCollection();
        private System.Timers.Timer myTimer = new System.Timers.Timer();
        private FileSystemWatcher incoming = new FileSystemWatcher();

        public Form1()
        {
            InitializeComponent();
        
            myTimer.Elapsed += new ElapsedEventHandler( ProcessQueuedFiles );
            myTimer.Interval = 10000;
            myTimer.Start();          
        }

        private void ProcessQueuedFiles(object source, ElapsedEventArgs e)
        {
            if (strListFilesToUpdate.Count > 0)
            {
                try
                {
                    toolStripStatusLabel1.Text = "Processing files...";
                    int count = 1;
                    int total = strListFilesToUpdate.Count;
                    foreach (string fileName in strListFilesToUpdate)
                    {
                        textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Uploading Changes for " + fileName);
                        toolStripStatusLabel1.Text = "Uploading " + count + " of " + total;
                        UploadFile(fileName);
                        int updated_index = strListFilesToUpdate.IndexOf(fileName);
                        count = count + 1;
                    }
                    strListFilesToUpdate.Clear();
                }
                catch (WebException ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
            toolStripStatusLabel1.Text = "Ready...";
        }


        private void Form1_InitializeSavedValues()
        {
            txtEmail.Text = Properties.Settings.Default.registered_email;
            txtWowLocation.Text = Properties.Settings.Default.wow_location;
            txtAccessKey.Text = Properties.Settings.Default.profile_access_key;
            label9.Text = "Application Version: " + Properties.Settings.Default.version;
            comboBox1.SelectedIndex = comboBox1.Items.IndexOf(Properties.Settings.Default.server_region);

            // check if the dir exists
            if (Directory.Exists(txtWowLocation.Text) == false)
            {
                textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "The provided location of the World of Warcraft directory does not exist. The uploader will not function properly.");
            }
            else
            {
                if (txtWowLocation.Text.Substring(txtWowLocation.Text.Length - 1, 1) != "\\")
                {
                    txtWowLocation.Text += "\\";
                }

                // setup the file watcher
                incoming.Path = txtWowLocation.Text + "wtf\\Account\\";
                incoming.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Attributes | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;        
                textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Change Monitoring Initialized");
                incoming.Filter = "GuildLaunchProfiler.lua";
                incoming.IncludeSubdirectories = true;
                incoming.Changed += new FileSystemEventHandler(OnChanged);
                incoming.Created += new FileSystemEventHandler(OnCreated);
                incoming.EnableRaisingEvents = true;

                // initialize the watches
                InitializeWatches();
            }          
        }

        private void InitializeWatches()
        {
            if (Directory.Exists(txtWowLocation.Text))
            {              
                // clear the exsiting characters
                clbCharacters.Items.Clear();

                textBox2.Text = "";
                textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Reinitializing Character Upload Status ===========================================");
                StringCollection aryCharactersSelected = Properties.Settings.Default.character_selections;
                if (txtWowLocation.Text != "")
                {
                    try
                    {                       
                        DirectoryInfo account_dir = new DirectoryInfo(txtWowLocation.Text + "wtf\\Account");
                        foreach (DirectoryInfo dlAccount in account_dir.GetDirectories())
                        {
                            string account_name = dlAccount.Name;
                            DirectoryInfo server_info = new DirectoryInfo(txtWowLocation.Text + "wtf\\Account\\" + account_name);
                            foreach (DirectoryInfo dlServer in server_info.GetDirectories())
                            {
                                if (dlServer.Name != "SavedVariables")
                                {
                                    string server_name = account_name + "\\" + dlServer.Name;
                                    DirectoryInfo characer_dir = new DirectoryInfo(txtWowLocation.Text + "wtf\\Account\\" + server_name);
                                    foreach (DirectoryInfo dlCharacter in characer_dir.GetDirectories())
                                    {
                                        string character_info = server_name + "\\" + dlCharacter.Name;
                                        //textBox2.Text = textBox2.Text + character_info + " - Current Value\r\n";
                                        if (aryCharactersSelected.Contains(character_info))
                                        {
                                            clbCharacters.Items.Add(character_info, true);
                                            //MessageBox.Show(txtWowLocation.Text + "wtf\\Account\\" + character_info + "\\SavedVariables\\");
                                            AddWatch(txtWowLocation.Text + "wtf\\Account\\" + character_info + "\\SavedVariables\\", dlAccount.Name, dlServer.Name, dlCharacter.Name);
                                        }
                                        else
                                        {
                                            clbCharacters.Items.Add(character_info, false);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (DirectoryNotFoundException ex)
                    {
                        textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Configured WoW Directory Not Found - Check Your Config");
                    }
                }
            }
        }

        private void Form1_InitializeApplicationValues()
        {
            linkLabel1.Links.Add(0,14,Properties.Settings.Default.get_access_key_url);
            linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
        }

        private void linkLabel1_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
        {
            // Determine which link was clicked within the LinkLabel.
            linkLabel1.Links[linkLabel1.Links.IndexOf(e.Link)].Visited = true;
            // Display the appropriate link based on the value of the 
            // LinkData property of the Link object.
            try
            {                
                System.Diagnostics.Process.Start(e.Link.LinkData.ToString());
            }
            catch (Exception ex)
            {
                MessageBox.Show("The application cannot start your default browser. This could be because you do not have one, or the application does not have the proper permissions to invoke the browser. To retrieve your client access key please go to:\n\n" + e.Link.LinkData.ToString());
            }
        } 

        private void AddWatch(string watchLocation, string account, string server, string character)
        {
            textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Watching for: " + watchLocation + "GuildLaunchProfiler.lua");
            watchedFiles.Add(watchLocation + "GuildLaunchProfiler.lua");
            textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Ready to upload " + character + " on " + server + " realm from your " + account + " account.");
        }



        private void OnChanged(object source, FileSystemEventArgs e)
        {
            // file modifcations will often send two requests, particularly the ones we are looking for, 
            // so we supress one and handle the second        
            iFileSystemNotification = iFileSystemNotification + 1;
            if (iFileSystemNotification == 2)
            {
                //textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Changed " + e.FullPath);
                if (strListFilesToUpdate.Contains(e.FullPath) != true)
                {
                    if (watchedFiles.Contains(e.FullPath))
                    {                        
                        strListFilesToUpdate.Add(e.FullPath);
                        textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), e.FullPath + " Changed");
                        toolStripStatusLabel1.Text = "Files Ready for Upload";
                    }
                }
                iFileSystemNotification = 0;
            }
        }

        private void OnCreated(object source, FileSystemEventArgs e)
        {
            // file modifcations will often send two requests, particularly the ones we are looking for, 
            // so we supress one and handle the second        
            textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), "Changed " + e.FullPath);
            if (strListFilesToUpdate.Contains(e.FullPath) != true)
            {
                if (watchedFiles.Contains(e.FullPath))
                {
                    strListFilesToUpdate.Add(e.FullPath);
                    textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), e.FullPath + " Changed");
                    toolStripStatusLabel1.Text = "Files Ready for Upload";
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Form1_InitializeSavedValues();
            Form1_InitializeApplicationValues();
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void button2_Click(object sender, EventArgs e)
        {
            SaveCurrentConfig();
            InitializeWatches();
            Form1_InitializeSavedValues();
        }

        private void SaveCurrentConfig()
        {
            StringCollection Items = new StringCollection();
            foreach (object itemChecked in clbCharacters.CheckedItems)
            {
                Items.Add(itemChecked.ToString());
            }
            Properties.Settings.Default.character_selections = Items;
            if (txtWowLocation.Text.Length > 0 && txtWowLocation.Text.Substring(txtWowLocation.Text.Length - 1, 1) != "\\")
            {
                txtWowLocation.Text += "\\";
            }
            Properties.Settings.Default.wow_location = txtWowLocation.Text;
            Properties.Settings.Default.registered_email = txtEmail.Text;
            if (isEmail(txtEmail.Text) == false)
            {
                MessageBox.Show("The provided email address does not appear to be valid. Please check the email address.");
            }
            Properties.Settings.Default.profile_access_key = txtAccessKey.Text;
            if (txtAccessKey.Text.Length < 35)
            {
                MessageBox.Show("The provided access key does not appear to be valid. Please check the access key or use the 'Get Access Key' link to get one.");
            }

            if ((comboBox1.SelectedIndex == -1) || (comboBox1.Items[comboBox1.SelectedIndex].ToString() != "US"
                && comboBox1.Items[comboBox1.SelectedIndex].ToString() != "EU - English"
                && comboBox1.Items[comboBox1.SelectedIndex].ToString() != "EU - French"
                && comboBox1.Items[comboBox1.SelectedIndex].ToString() != "EU - German"
                && comboBox1.Items[comboBox1.SelectedIndex].ToString() != "EU - Spanish"))
            {
                Properties.Settings.Default.server_region = "US";
                MessageBox.Show("The provided region does not appear to be valid. Please select a region from the dropdown list.");
            }
            else
            {
               Properties.Settings.Default.server_region = comboBox1.Items[comboBox1.SelectedIndex].ToString();
            }
            Properties.Settings.Default.Save();
        }

        public delegate void UpdateStatustCallback(string text);

        private void UpdateStatusMessage(string text)
        {
            toolStripStatusLabel1.Text = text;
        }

        public delegate void UpdateTextCallback(string text);

        private void UpdateStatusWindow(string text)
        {
            textBox2.Text = text + "\r\n" + textBox2.Text;
        }

        public static string UploadFileEx(string uploadfile, string url,string fileFormName, string contenttype,NameValueCollection querystring,CookieContainer cookies)   
         {   
             if( (fileFormName== null) ||   
                 (fileFormName.Length ==0))   
             {   
                 fileFormName = "file";   
             }   
   
             if( (contenttype== null) ||   
                 (contenttype.Length ==0))   
             {   
                 contenttype = "application/octet-stream";   
             }   
   
   
             string postdata;   
             postdata = "?";   
             if (querystring!=null)   
             {   
                 foreach(string key in querystring.Keys)   
                 {   
                     postdata+= key +"=" + querystring.Get(key)+"&";   
                 }   
             }   
             Uri uri = new Uri(url+postdata);   
   
   
             string boundary = "----------" + DateTime.Now.Ticks.ToString("x");   
             HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(uri);   
             webrequest.CookieContainer = cookies;   
             webrequest.ContentType = "multipart/form-data; boundary=" + boundary;   
             webrequest.Method = "POST";   
   
   
             // Build up the post message header   
             StringBuilder sb = new StringBuilder();   
             sb.Append("--");   
             sb.Append(boundary);   
             sb.Append("\r\n");   
             sb.Append("Content-Disposition: form-data; name=\"");   
             sb.Append(fileFormName);   
             sb.Append("\"; filename=\"");   
             sb.Append(Path.GetFileName(uploadfile));   
             sb.Append("\"");   
             sb.Append("\r\n");   
             sb.Append("Content-Type: ");   
             sb.Append(contenttype);   
             sb.Append("\r\n");   
             sb.Append("\r\n");             
   
             string postHeader = sb.ToString();   
             byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);   
   
             // Build the trailing boundary string as a byte array   
            // ensuring the boundary appears on a line by itself   
             byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");   
   
             FileStream fileStream = new FileStream(uploadfile, FileMode.Open, FileAccess.Read, FileShare.Read);   
             long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;   
             webrequest.ContentLength = length;   
   
             Stream requestStream = webrequest.GetRequestStream();   
   
             // Write out our post header   
             requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);   
   
             // Write out the file contents   
             byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];   
             int bytesRead = 0;   
             while ( (bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 )   
                 requestStream.Write(buffer, 0, bytesRead);
             fileStream.Close();
             // Write out the trailing boundary   
             requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);   
             WebResponse responce = webrequest.GetResponse();   
             Stream s = responce.GetResponseStream();   
             StreamReader sr = new StreamReader(s);   
   
             return sr.ReadToEnd();   
         }

         private void UploadFile(string file_name)   
         {   
             CookieContainer cookies = new CookieContainer();   
             //add or use cookies   
   
             NameValueCollection querystring = new NameValueCollection();   
                
             // simulate this form    
             //<form action ="http://localhost/test.php" method = POST>   
             //<input type = text name = uname>   
             //<input type = password name =passwd>   
             //<input type = FILE name = uploadfile>   
             //<input type=submit>   

             //Properties.Settings.Default.registered_email = txtEmail.Text;
             //Properties.Settings.Default.profile_access_key = txtAccessKey.Text;  
             querystring["registered_email"] = Properties.Settings.Default.registered_email;
             querystring["access_key"] = Properties.Settings.Default.profile_access_key;
             querystring["server_region"] = Properties.Settings.Default.server_region;
             querystring["character_info"] = GetStringBetween(file_name, "\\account\\", "\\SavedVariables\\GuildLaunchProfiler.lua").Replace("\\", "_");
   
             string uploadfile;// set to file to upload   

             uploadfile = file_name;   

             //everything except upload file and url can be left blank if needed   

             string result = UploadFileEx(uploadfile, Properties.Settings.Default.upload_url, "uploadfile", "text/html", querystring, cookies);
             textBox2.Invoke(new UpdateTextCallback(this.UpdateStatusWindow), result);        
   
                 /*  
                 *  
                 contents of test.php  
                 <?php  
  
                 print_r($_REQUEST);  
  
                 $uploadDir = '%SOMEPATH';  
                 $uploadFile = $uploadDir . $_FILES['userfile']['name'];  
                 print "<pre>";  
                 if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadFile))  
                 {  
                     print "File is valid, and was successfully uploaded. ";  
                 }  
                 else  
                 {  
                     print "Possible file upload attack!  Here's some debugging info:\n";  
                     print_r($_FILES);  
                 }  
                 print "</pre>";  
  
                 ?>*/  
         }

         public static string GetStringBetween(string stream, string from, string to)
         {
             try
             {
                 string subField = string.Empty;
                 subField = RightOf(stream, from);
                 subField = LeftOf(subField, to);
                 return subField;
             }
             catch (Exception ex)
             {
                 //ErrorTool.ProcessError(ex);
                 return null;
             }
         }

         /// <summary>
         /// Will return the text to the LEFT of indicated substring 
         /// </summary>
         /// <param name="stream">
         /// string from which to cull a portion of text
         /// </param>
         /// <param name="stringToStopAt">
         /// string that indicates what char or string to stop at
         /// </param>
         /// <returns>
         /// The string to the left of point x (stringToStopAt)
         /// </returns>
         public static string LeftOf(string stream, string stringToStopAt)
         {
             try
             {
                 if (stringToStopAt == null || stringToStopAt == string.Empty)
                     return stream;


                 int stringLength = stream.Length;
                 int findLength = stringToStopAt.Length;


                 stringToStopAt = stringToStopAt.ToLower();
                 string temp = stream.ToLower();
                 int i = temp.IndexOf(stringToStopAt);


                 if ((i <= -1) && (stringToStopAt != temp.Substring(0, findLength))
                     || (i == -1))
                     return stream;


                 string result = stream.Substring(0, i);
                 return result;
             }
             catch (Exception ex)
             {
                 //ErrorTool.ProcessError(ex);
                 return null;
             }
         }


         /// <summary>
         /// Will return the text to the RIGHT of whatever substring you indicate
         /// </summary>
         /// <param name="stream">
         /// string from which to cull a portion of text
         /// </param>
         /// <param name="stringToStartAfter">
         /// string that indicates what char or string to start after
         /// </param>
         /// <returns>
         /// The string to the right of point x (stringToStartAfter)
         /// </returns>
         public static string RightOf(string stream, string stringToStartAfter)
         {
             try
             {
                 if (stringToStartAfter == null || stringToStartAfter == string.Empty)
                     return stream;
                 stringToStartAfter = stringToStartAfter.ToLower();
                 string temp = stream.ToLower();
                 int findLength = stringToStartAfter.Length;
                 int i = temp.IndexOf(stringToStartAfter);
                 if ((i <= -1) && (stringToStartAfter != temp.Substring(0, findLength))
                     || (i == -1))
                     return stream;

                 string result =
                     stream.Substring(i + findLength, stream.Length - (i + findLength));
                 return result;
             }
             catch (Exception ex)
             {
                 //ErrorTool.ProcessError(ex);
                 return null;
             }
         }

         private void notifyIcon1_DoubleClick(object sender, EventArgs e)
         {
             Show();
             WindowState = FormWindowState.Normal;
         }

         private void Form1_Resize(object sender, EventArgs e)
         {
             if (FormWindowState.Minimized == WindowState)
                 Hide();
         }

         private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
         {

         }

         private void toolStripMenuItem1_Click(object sender, EventArgs e)
         {
             Show();
             WindowState = FormWindowState.Normal;
         }

         private void toolStripMenuItem2_Click(object sender, EventArgs e)
         {
             Close();
         }

         private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
         {
            System.Diagnostics.Process.Start("http://www.guildlaunch.com/");       
         }

         private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
         {
             System.Diagnostics.Process.Start(linkLabel3.Text);       
         }

         private void button1_Click(object sender, EventArgs e)
         {
             folderBrowserDialog1.ShowNewFolderButton = false;
             folderBrowserDialog1.Description = "Select the Location of the 'World of Warcraft' Directory";
             if (folderBrowserDialog1.ShowDialog()==System.Windows.Forms.DialogResult.OK)
             {
                 txtWowLocation.Text = folderBrowserDialog1.SelectedPath;
             }
         }

         private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
         {

         }

         private void button3_Click(object sender, EventArgs e)
         {
             RegistryKey key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Blizzard Entertainment\\World of Warcraft");
             if (key != null)
             {
                 if (key.GetValue("InstallPath") != null)
                 {
                     string keyvalue = (string)key.GetValue("InstallPath");
                     string wowLocation = keyvalue.Substring(0, keyvalue.Length - 1);
                     txtWowLocation.Text = wowLocation;
                 }
             }             
         }

        /* HELPER FUNCTIONS */

    public static bool isEmail(string inputEmail)
    {
        if (inputEmail == null || inputEmail.Length == 0)
        {
            inputEmail = "";
        }

         string strRegex = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +
               @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
               @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
         Regex re = new Regex(strRegex);
         if (re.IsMatch(inputEmail))
             return (true);
         else
             return (false);
    }

    private void linkLabel1_LinkClicked_1(object sender, LinkLabelLinkClickedEventArgs e)
    {

    }
  }
}
